WebWork 2 : Using Checkboxes - User.java
This page last changed on Jun 18, 2004 by plightbo.
package cash.model; import net.sf.hibernate.HibernateException; import org.apache.log4j.Logger; import java.security.GeneralSecurityException; import java.security.MessageDigest; import java.security.SecureRandom; import java.util.ArrayList; import java.util.Date; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Set; import java.util.SortedSet; import java.util.TimeZone; import java.util.TreeSet; import cash.config.ConfigManager; import cash.util.Hex; import cash.util.HibernateUtil; import cash.util.UtcDate; import cash.validator.PasswordFormatValidator; /** * Represents a User object. Clients of this class should instantiate a User object with the * multi-arg constructor rather than using setters. * * @author Joel Hockey * @version $Id: Using\040Checkboxes\040-\040User.java.html,v 1.1 2004/12/14 22:47:44 plightbo Exp $ * @hibernate.class * table="user" * dynamic-update="true" * optimistic-lock="version" */ public class User implements java.io.Serializable { private static final Logger LOG = Logger.getLogger(User.class); private static MessageDigest s_md5; private static SecureRandom s_random; private static final int MAX_LOGIN_FAILURE_COUNT = 20; private static final boolean RESET_LOCKED_OUT_AFTER_TIME = true; private static final long RESET_LOCKED_OUT_TIME = 1 * 60 * 60 * 1000; // 1 hour private int m_id; private int m_version; private String m_username; private String m_password; private Date m_passwordChangeDate; private String m_hashedPassword; private SortedSet m_passwordHistory = new TreeSet(); private String m_salt; private byte[] m_saltBytes; private Date m_createDate; private String m_email; private Locale m_locale; private TimeZone m_timeZone; private String m_telephone; private Date m_lastSuccessfulLogin; private String m_lastSuccessfulLoginIp; private Date m_lastFailedLogin; private String m_lastFailedLoginIp; private int m_loginFailureCount; private int m_maxLoginFailureCount = MAX_LOGIN_FAILURE_COUNT; private boolean m_resetLockedOutAfterTime = RESET_LOCKED_OUT_AFTER_TIME; private long m_resetLockedOutTime = RESET_LOCKED_OUT_TIME; private boolean m_lockedOut = false; private boolean m_disabled = false; private boolean m_isSuperUser = false; private boolean m_passwordNeverExpires = false; private Set m_privileges = new HashSet(); static { try { s_md5 = MessageDigest.getInstance("MD5"); s_random = SecureRandom.getInstance("SHA1PRNG"); } catch (GeneralSecurityException gse) { // shouldn't happen LOG.error("Error creating MD5 or SHA1PRNG", gse); throw new RuntimeException("Error creating MD5 or SHA1PRNG"); } } /** default constructor for Hibernate */ public User() { } /** * Create a User. * * @param username The username for logging in * @param password The user's password * @param email The user's email * @throws InvalidPasswordException if password is invalid. */ public User(String username, String password, String email) throws InvalidPasswordException { m_username = username; // password initSalt(); if (!PasswordFormatValidator.checkPasswordFormat(password)) { throw new InvalidPasswordException(); } m_hashedPassword = hashPassword(password); m_createDate = UtcDate.createUtcDate(); m_email = email; m_locale = Locale.getDefault(); m_timeZone = TimeZone.getDefault(); } /** @param id The id to set */ public void setId(int id) { m_id = id; } /** * @return unique id of this User. Generated by DB. * @hibernate.id * generator-class="native" */ public int getId() { return m_id; } /** @param version The version of this object */ public void setVersion(int version) { m_version = version; } /** * @return version of this object * @hibernate.version */ public int getVersion() { return m_version; } /** @param username The username to set */ public void setUsername(String username) { m_username = username; } /** * @return username * @hibernate.property * length="32" * unique="true" * not-null="true" */ public String getUsername() { return m_username; } /** * Set's the user's password without updating history or checking validity. * This should only be used at User creation time, and password validity * should be checked externally to this method. * Do not use to update password, see {@link #changePassword(String)} * @param password user's password */ public void setPassword(String password) { m_password = password; if (m_salt == null) { initSalt(); } m_hashedPassword = hashPassword(password); m_passwordChangeDate = UtcDate.createUtcDate(); } /** * This method is provided to help at User creation time. It will only return * valid values if {@link #setPassword(String)} has already been called. * @return plaintext password. */ public String getPassword() { return m_password; } /** @param time Date (UTC) user last changed password. */ public void setPasswordChangeDate(Date time) { m_passwordChangeDate = time; } /** * @return UTC date of last password change * @hibernate.property * type="cash.model.TimestampType" * length="23" */ public Date getPasswordChangeDate() { return m_passwordChangeDate; } /** * Sets the user's hashed password. This method is provided only for the use * of hibernate. Users of this class should not call this method. * Use the {@link #setPassword(String)} method to set the plaintext password. * @param hash The hashed password to set */ public void setHashedPassword(String hash) { m_hashedPassword = hash; } /** * @return hashed password * @hibernate.property * column="pwd" * length="32" * not-null="true" */ public String getHashedPassword() { return m_hashedPassword; } /** * @param oldPasswords The last n passwords, where n * is defined as noRepeatHistory in User configuration. Passwords are ordered * in descending order of creation. */ public void setPasswordHistory(SortedSet oldPasswords) { m_passwordHistory = oldPasswords; } /** * @return Password history * @hibernate.set * lazy="true" * sort="cash.model.PasswordHistory" * inverse="true" * cascade="all" * @hibernate.collection-key * column="userId" * @hibernate.collection-one-to-many * class="cash.model.PasswordHistory" */ public SortedSet getPasswordHistory() { return m_passwordHistory; } /** @param random The random salt to be used with password */ public void setSalt(String random) { m_salt = random; m_saltBytes = Hex.fromString(random); } /** * @return random salt used with password * @hibernate.property * length="32" * not-null="true" */ public String getSalt() { return m_salt; } /** @param time create date */ public void setCreateDate(Date time) { m_createDate = time; } /** * @return Date in UTC user was created. * @hibernate.property * update="false" * not-null="true" * type="cash.model.TimestampType" * length="23" */ public Date getCreateDate() { return m_createDate; } /** @param email User's email */ public void setEmail(String email) { m_email = email; } /** * @return User's email * @hibernate.property * length="255" * not-null="true" */ public String getEmail() { return m_email; } /** @param locale The User's locale. This should be a 2 character field. */ public void setLocale(Locale locale) { m_locale = locale; } /** * @return User's locale. Uses 2 character ISO-something value. * @hibernate.property * not-null="true" */ public Locale getLocale() { return m_locale; } /** @param timeZone User's time zone */ public void setTimeZone(TimeZone timeZone) { m_timeZone = timeZone; } /** * @return User's timezone * @hibernate.property * not-null="true" */ public TimeZone getTimeZone() { return m_timeZone; } /** @param telephone User's telephone */ public void setTelephone(String telephone) { m_telephone = telephone; } /** * @return Telephone of user * @hibernate.property * length="16" */ public String getTelephone() { return m_telephone; } /** @param time user's last successful login date in UTC. */ public void setLastSuccessfulLogin(Date time) { m_lastSuccessfulLogin = time; } /** * @return UTC date of last successful login * @hibernate.property * type="cash.model.TimestampType" * length="23" */ public Date getLastSuccessfulLogin() { return m_lastSuccessfulLogin; } /** @param ip IP address used for user's last successful login. */ public void setLastSuccessfulLoginIp(String ip) { m_lastSuccessfulLoginIp = ip; } /** * @return IP address used for last successful login * @hibernate.property */ public String getLastSuccessfulLoginIp() { return m_lastSuccessfulLoginIp; } /** @param time user's last failed login date in UTC. */ public void setLastFailedLogin(Date time) { m_lastFailedLogin = time; } /** * @return UTC date of last failed login * @hibernate.property * type="cash.model.TimestampType" * length="23" */ public Date getLastFailedLogin() { return m_lastFailedLogin; } /** @param ip IP address used for user's last failed login. */ public void setLastFailedLoginIp(String ip) { m_lastFailedLoginIp = ip; } /** * @return IP address used for last failed login * @hibernate.property */ public String getLastFailedLoginIp() { return m_lastFailedLoginIp; } /** * Sets the number of times that a user has failed when attempting to login. * This value is reset when a user logs in successfully, or their account is reset. * @param count the value to set. */ public void setLoginFailureCount(int count) { m_loginFailureCount = count; } /** * @return The number of times that a user has failed when attempting to login. * This value is reset when a user logs on successfully, or their account is reset. * @hibernate.property */ public int getLoginFailureCount() { return m_loginFailureCount; } /** * @param count The maximum number of times that a user may fail to login before * their account is locked out */ public void setMaxLoginFailureCount(int count) { m_maxLoginFailureCount = count; } /** * @return The maximum number of times that a user may fail to login before their account * is locked out. * @hibernate.property */ public int getMaxLoginFailureCount() { return m_maxLoginFailureCount; } /** * @param reset Whether this user's account will be unlocked after a specified time when it is locked * due to login failure. * @see #setResetLockedOutAfterTime(boolean) setResetLockedOutAfterTime */ public void setResetLockedOutAfterTime(boolean reset) { m_resetLockedOutAfterTime = reset; } /** * @return Whether this user's account will be unlocked after a specified time when it * is locked out due to login failure. * @see #getResetLockedOutAfterTime getResetLockedOutAfterTime * @hibernate.property */ public boolean getResetLockedOutAfterTime() { return m_resetLockedOutAfterTime; } /** * @param time The time in millis between login attempts before login failure count is reset. Login failure * count will only be reset if the Reset Locked Out After Time boolean is set to true. */ public void setResetLockedOutTime(long time) { m_resetLockedOutTime = time; } /** * @return Time in milliseconds before account is auto-reset after login lockout. * @hibernate.property */ public long getResetLockedOutTime() { return m_resetLockedOutTime; } /** @param lockedOut User's locked out status. */ public void setLockedOut(boolean lockedOut) { m_lockedOut = lockedOut; } /** * @return Whether this user's account is locked out * @hibernate.property */ public boolean isLockedOut() { return m_lockedOut; } /** @param disabled User's disabled status. */ public void setDisabled(boolean disabled) { m_disabled = disabled; } /** * @return Whether this user's account disabled * @hibernate.property */ public boolean isDisabled() { return m_disabled; } /** @param superUser True if user is super user */ public void setSuperUser(boolean superUser) { m_isSuperUser = superUser; } /** * @return Whether this user is a super user * @hibernate.property */ public boolean isSuperUser() { return m_isSuperUser; } /** @param expires True if user's password never expires */ public void setPasswordNeverExpires(boolean expires) { m_passwordNeverExpires = expires; } /** * @return Whether this user's password ever expires * @hibernate.property */ public boolean getPasswordNeverExpires() { return m_passwordNeverExpires; } /** @param privs Set of privileges for this user */ public void setPrivileges(Set privs) { m_privileges = privs; } /** * @return Set of Privileges for this User. * @hibernate.set * table="user_priv" * lazy="true" * cascade="all" * @hibernate.collection-key * column="userId" * @hibernate.collection-element * column="priv" * type="string" */ public Set getPrivileges() { return m_privileges; } /** convenience method of OGNL */ public void setPriv(String[] privs) { for (int i = 0; i < privs.length; i++) { m_privileges.add(privs[i]); } } // other methods /** * Changes the user's password. Password must meet criteria * defined in configuration. The user's password will be appended to * a random 20 byte salt and then hashed using MD5 to create the * value that will be stored in the DB. The current Hibernate Session * will be used to update pwd history. * * @param password The password to set * @return true if password is changed, false if password was not changed * because it did not meet password requirements. * @throws HibernateException if error updating password history */ public boolean changePassword(String password) throws HibernateException { // check format if (!PasswordFormatValidator.checkPasswordFormat(password)) { return false; } // check history // first check current password String hashedPwd = hashPassword(password); LOG.debug("checking if password is same as current"); if (hashedPwd.equals(m_hashedPassword)) { LOG.info("password is same as current password"); return false; } LOG.debug("checking if password exists in history. History size is " + m_passwordHistory.size()); for (Iterator i = getPasswordHistory().iterator(); i.hasNext(); ) { PasswordHistory ph = (PasswordHistory)i.next(); if (hashedPwd.equals(ph.getHashedPassword())) { LOG.info("password already used as one of last " + ConfigManager.getConfig().getUser().getNoRepeatHistory()); return false; } } // add current pwd to history and truncate history if it is too long now PasswordHistory ph = new PasswordHistory(this, m_hashedPassword); m_passwordHistory.add(ph); LOG.debug("saving old password into password history"); HibernateUtil.currentSession().save(ph); // compare to (noRepeat - 1) because we are checking current as part of history if (m_passwordHistory.size() > ConfigManager.getConfig().getUser().getNoRepeatHistory() - 1) { PasswordHistory toRemove = (PasswordHistory)m_passwordHistory.first(); LOG.info("Removing password history object for user " + m_username + " created: " + toRemove.getCreateDate()); m_passwordHistory.remove(toRemove); HibernateUtil.currentSession().delete(toRemove); } // now set password and date m_hashedPassword = hashedPwd; m_passwordChangeDate = UtcDate.createUtcDate(); return true; } /** * Hashes input pwd to see if it equals stored pwd hash value. * @param pwd Password to check * @return true if passwords are equal. */ public boolean passwordEquals(String pwd) { String hash = hashPassword(pwd); return m_hashedPassword.equalsIgnoreCase(hash); } /** * Hashes salt and password to produce hashed password. * @param pwd Password to hash * @return Hex encoding of MD5 hash of salt and pwd */ private String hashPassword(String pwd) { byte[] pwdBytes = pwd.getBytes(); //TODO: should an encoding be specified here? byte[] in = new byte[OS:m_saltBytes.length + pwdBytes.length]; System.arraycopy(m_saltBytes, 0, in, 0, m_saltBytes.length); System.arraycopy(pwdBytes, 0, in, m_saltBytes.length, pwdBytes.length); byte[] out = s_md5.digest(in); return Hex.toString(out); } /** initialises salt */ private void initSalt() { m_saltBytes = new byte[OS:16]; s_random.nextBytes(m_saltBytes); m_salt = Hex.toString(m_saltBytes); } /** @return String representation of User */ public String toString() { StringBuffer sb = new StringBuffer(500); sb.append("[").append("ID:").append(m_id) .append(",version:").append(m_version) .append(",hashedPassword:").append(m_hashedPassword) .append(",salt:").append(m_salt) .append(",createDate:").append(m_createDate) .append(",email:").append(m_email) .append(",locale:").append(m_locale) .append(",timeZone:").append(m_timeZone) .append(",telephone:").append(m_telephone) .append(",lastSuccessfulLogin:").append(m_lastSuccessfulLogin) .append(",lastSuccessfulLoginIp:").append(m_lastSuccessfulLoginIp) .append(",lastFailedLogin:").append(m_lastFailedLogin) .append(",lastFailedLoginIp:").append(m_lastFailedLoginIp) .append(",loginFailureCount:").append(m_loginFailureCount) .append(",maxLoginFailureCount:").append(m_maxLoginFailureCount) .append(",resetLockedOutAfterTime:").append(m_resetLockedOutAfterTime) .append(",resetLockedOutTime:").append(m_resetLockedOutTime) .append(",lockedOut:").append(m_lockedOut) .append(",disabled:").append(m_disabled) .append(",isSuperUser:").append(m_isSuperUser) .append(",passwordNeverExpires:").append(m_passwordNeverExpires) .append(",passwordChangeDate:").append(m_passwordChangeDate) .append(",privs:").append(m_privileges); return sb.toString(); } /** * Copies editable data from this object to User object provided. This is used * in Edit actions. Not all fields are copied, only those that are editable * @param user Object to copy to */ public void copy(User user) { user.setUsername(m_username); user.setEmail(m_email); user.setLocale(m_locale); user.setTimeZone(m_timeZone); user.setTelephone(m_telephone); user.setLockedOut(m_lockedOut); user.setDisabled(m_disabled); user.setPasswordNeverExpires(m_passwordNeverExpires); // do some smarts for privs removal. Clear all if more than half are removed if (m_privileges.size() <= user.getPrivileges().size() / 2) { LOG.debug("detected that many privs are removed, clearing all"); user.setPrivileges(m_privileges); } else { // find which ones should be removed List toRemove = new ArrayList(); for (Iterator i = user.getPrivileges().iterator(); i.hasNext(); ) { String priv = (String)i.next(); if (!m_privileges.contains(priv)) { toRemove.add(priv); } } // remove them for (int i = 0; i < toRemove.size(); i++) { user.getPrivileges().remove(toRemove.get(i)); } // add all new privs for (Iterator i = m_privileges.iterator(); i.hasNext(); ) { user.getPrivileges().add(i.next()); } } } } |
Document generated by Confluence on Dec 14, 2004 16:36 |